#ifndef _FUNCTION_MODEL_H_
#define _FUNCTION_MODEL_H_

#include "table.h"
#include "table-structures.h"
#include "problem.h"
#include "restaurant.h"
#include "graphdl.h"

void InitFunctionModel();


/* 
   execution: (<call> <definition> <dependencies>)
   call: (function args result)
   dependencies: hash from (function args) -> call

   Function Model is a hash that contains:
   MODELDL
   (XTIME ,function ,args) -> (execution time)

   Tablesets:
   (FUNCTION_DEFINITIONS ,function)
   (FUNCTION_ARGS ,function)     // excludes problem args
   FUNCTIONS
   (DEPENDENTS ,call)
*/

// Two kinds of errors are allowed in the model.
//  MISSING_CALLS - 
//     This call does not exist, but someone depends on it,
//     or it is required by the problem.
//  EARLY_CALLS - 
//     time is not after the time of all dependencies.

// Special rules for problem arguments
// If there are no calls involving a problem argument, one will
//   be added for you. The added implicit call is correct for 
//   TRAINING arguments but not necessarily for others.
// Depending on an implicit call can never make you early
// We can never add calls that conflict with the required values
//   for TRAINING arguments.

struct FunctionModel {
  FunctionModel(Problem *problem, Table<Thing> *table)
  : revision_(0), problem_(problem), base_(NULL), table_(table) { 
    gdl_.Init(table_, MODELDL);
  }

  void AddBase(FunctionModel *base);

  // Call this for a new model
  void InitNewModel();
  int64 GetDL();
  void UpdateDL(int64 delta);
  int64 Utility();

  // returns NULL on missing
  Thing GetXTime(Thing function, Thing args);
  Thing CallGetXTime(Thing call);
  Thing CallGetTime(Thing call);


  // xtime is already verified.
  // clobbers all in its path
  void AddXTime(Thing xtime, ThreadAllocator *a);
  void ReplaceXTimeSameResult(Thing old_xtime, Thing xtime);
  void SimpleAddXTime(Thing xtime, int64 delta);

  // Verifies that the xtime exists in the model
  bool RemoveXTime(Thing xtime, ThreadAllocator *a);

  void AddFunction(Thing function, int64 delta);
  void AddFunctionArgs(Thing function, Thing args, int64 delta);
  void AddFunctionDefinition(Thing function, Thing definition, int64 delta);
  void AddDependencies(Thing call, Thing dependencies, int64 delta);
  // returns an implicit result, or NULL
  Thing ImplicitResult(Thing fname, Thing args);

  // --------------- Low level error updating ------------------

  // The time of this call has changed. 
  // Changes early status of call
  // Changes early status of dependents
  void UpdateErrorsTimeChanged(Thing call);

  // The existence of this call has changed. (implicit calls count as calls)
  // everything that UpdateErrorsTimeChange does, plus
  // Changes missing status of call
  void UpdateErrorsExistenceChanged(Thing call);

  void UpdateMissing(Thing call);
  int64 IsMissing(Thing call);
  void UpdateEarly(Thing call);
  int64 IsEarly(Thing call);
  
  // the word Current implies involves the implicit calls

  // What is the current model result, including implicit calls
  Thing Result(Thing function, Thing args);

  // Does a call exist, including implicitly in the model
  bool CallExists(Thing call);

  // Does a call not match the training data
  bool ConflictsWithTrainingData(Thing call);



  // Data
  uint64 revision_;
  Problem * problem_;      // The problem we're solving
  FunctionModel * base_;   // If we are an overlay this is our base
  bool locked_;            // Is this a locked overlay? 
  Table<Thing> * table_;   // Now owned, we store stuff here
  GraphDL gdl_;
};

// Holds the consensus model
struct Solution : public AllocatorDependant {
  Solution(Problem * problem);
  Solution(Problem * problem, string file_prefix);

  int64 Utility() { return model_.Utility(); }
  void Store(string prefix);

  // Deal with garbage collection
  int32 NumShards() { return 100; }
  void FlagReferences(int32 shard, Frontier * frontier);

  Problem *problem_;
  RWLock rw_;
  Table<Thing> table_;
  FunctionModel model_;
};

struct SolutionThread;

// for verifying an execution
struct ExecutionState {

  // does this execution check out? 
  bool Verify(Thing x);

  // used in generative mode.  Returns NULL on error. 
  Thing ModelCall(Thing function, Thing args);

  Thing dependencies_;
  SolutionThread *st_;
  ThreadAllocator * A();
  EconomyThread * ET(); 

  ExecutionState(SolutionThread *st); 
};

struct SolutionThread {
  SolutionThread(Solution *solution, EconomyThread *et) 
  : xs_(this),
    et_(et), 
    solution_(solution), 
    table_((1 << ConfigInt("overlay_lg_size")), true, &(solution_->table_)),
    overlay_(solution_->problem_, &table_),
    reader_(&(solution->rw_)) {}

  bool CacheVerifyExecution(Thing x);

  Thing Test(Thing maybe_force);

  ThreadAllocator * A();

  void Commit();
  void Globalize();
  bool BadOverlay();
  Thing BadOverlayCall();
  Thing SystemCall(Syscall which, int32 num_parameters);
  Thing TableSetCall(Thing action, Thing set_description, Thing param);
  Thing TableLookCall(Thing key, Thing default_result);
  Thing NewOverlayCall(Thing locked);
  Thing AddXTimeCall(Thing xtime);
  Thing RemoveXTimeCall(Thing xtime);
  FunctionModel * BaseModel();

  ExecutionState xs_;
  EconomyThread *et_;
  Solution *solution_;
  Table<Thing> table_;
  FunctionModel overlay_;
  RWLock::Reader reader_;
};

#endif
